import {
  isEmpty,
  isNull,
  PagedList,
  PageParams,
  DurationParams,
  isUndefined,
  numberToString,
  canUse,
  toString,
} from "mmb";
import * as messagepb from "../grpc-client/shared/message_pb";
import * as brandpb from "../grpc-client/service/brand_pb";
import {
  getBgsBrandOpSafely,
  getMetadata,
  getOptionsOfLight,
  wrapperGrpcError,
} from "../grpc-client/grpcAdapter";
import GrpcErrCode from "../grpc-client/grpcErrCode";
import DataSource from "../bg-enums/dataSource";
import BrandEndorseApplyField from "../bg-enums/brandEndorseApplyField";
import UserStratification from "../bg-enums/userStratification";
import { SyncAction } from "../bg-enums/sync";
import { genWebToken, UniqueTokenExpiredError } from "../proxy/webPosProxy";
import {
  productWithDistributeInfoPbToObject,
  pbTimestampToJs,
  jsTimestampToPb,
} from "./helper";
// import { getUser, updateMemberToken } from "./userService";
import { getCache, setCache, makeEntityKey } from "../proxy/redisProxy";
import { getBrandRemotePkCachelly } from "./syncService";
import {
  Brand,
  BilinBrand,
  BrandExt,
  BrandWithStat,
  BrandWithExtAndStat,
  BrandWithExtAndStatAndEndorsement,
  BrandDistributeSupplySetting,
  ActivityAsFeatureAvailable,
  GetBrandWithExtAndStatAndEndorsementForUserRet,
  BrandEndorsement,
  BrandDistributeEndorsement,
  OpenBrandParams,
  OpenNewParams,
  UpdateBrandParams,
  UpsertBrandEndorsementParams,
  UpsertBrandDistributeEndorsementParams,
  UpsertBrandDistributeSupplySettingParams,
  UpsertBrandDistributeProductParams,
  BatchUpsertBrandDistributeProductsParams,
  RefreshWebposTokenParams,
  BrandSettlementAccountInfo,
  BrandAccountWithdrawApplyment,
  CreateAccountWithdrawApplymentParams,
  AccountLogInfo,
  AggrBrandAccountLogsToInOutRet,
  ProductWithDistributeInfo,
} from "./declareTypes";
import ActivityType from "../bg-enums/activityType";
import DeliveryMode, { isDeliveryMode } from "../bg-enums/deliveryMode";
import BrandDistributionEndorseApplyField from "../bg-enums/brandDistributionEndorseApplyField";

/****************************************************************************************/
/*                                   brand entity                                       */
/****************************************************************************************/

const makeBrandCacheKey = makeEntityKey("brand");

const brandPbToObject = (pb: brandpb.BrandEntity): Brand => {
  return {
    brandId: `${pb.getId()}`,
    name: pb.getName(),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    remotePK: `${pb.getRemotePk()}`,
  };
};

const bilinBrandPbToObject = (pb: brandpb.BilinBrandEntity): BilinBrand => {
  return {
    remotePkOfBrand: toString(pb.getRemotePkOfBrand()),
    remotePkOfEmployee: toString(pb.getRemotePkOfEmployee()),
    name: pb.getName(),
    logo: pb.getLogo(),
    desc: pb.getDesc(),
  };
};

/****************************************************************************************/
/*                                   brand cmd                                          */
/****************************************************************************************/

export const openBrand = async ({
  origin,
  name,
  intro,
  pic,
  remotePK,
  userId,
}: OpenBrandParams): Promise<Brand> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.OpenBrandRequest();

  req.setOrigin(origin);
  req.setName(name);
  req.setIntro(intro);
  req.setPic(pic);
  req.setRemotePk(Number(remotePK));
  req.setUserId(Number(userId));

  let syncAction =
    origin === DataSource.LOCAL
      ? SyncAction.LOCAL_CREATE_TO_REMOTE
      : SyncAction.REMOTE_CREATE_TO_LOCAL;
  req.setSyncAction(syncAction);

  return new Promise((resolve, reject) => {
    cl.oldOpenBrand(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(brandPbToObject(res.getBrand()!));
    });
  });
};

export const openNew = async ({
  name,
  intro,
  pic,
  remotePkOfBrand,
  remotePkOfEmployee,
  userId,
}: OpenNewParams): Promise<Brand> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.OpenNewRequest();

  req.setName(name);
  req.setIntro(intro);
  req.setPic(pic);
  if (!isUndefined(remotePkOfBrand)) {
    req.setRemotePkOfBrand(Number(remotePkOfBrand));
  }

  if (!isUndefined(remotePkOfEmployee)) {
    req.setRemotePkOfEmpolyee(Number(remotePkOfEmployee));
  }
  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.openNew(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(brandPbToObject(res.getBrand()!));
    });
  });
};

export const updateBrand = async ({
  brandId,
  intro,
  pic,
  homepageBg,
  tel,
}: UpdateBrandParams): Promise<Brand> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.UpdateBrandRequest();

  req.setId(Number(brandId));
  if (!isUndefined(intro)) {
    req.setIntro(intro);
  }
  if (!isUndefined(pic)) {
    req.setPic(pic);
  }
  if (!isUndefined(homepageBg)) {
    req.setHomepageBg(homepageBg);
  }
  if (!isUndefined(tel)) {
    req.setTel(tel);
  }

  return new Promise((resolve, reject) => {
    cl.updateBrand(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(brandPbToObject(res.getBrand()!));
    });
  });
};

/****************************************************************************************/
/*                                   brand query                                        */
/****************************************************************************************/

export const getCachedBrand = async (brandId: string): Promise<Brand> => {
  const k = makeBrandCacheKey(brandId);
  const cv = await getCache(k);
  if (canUse(cv)) {
    return JSON.parse(cv!) as Brand;
  }

  let brand = await getBrand(brandId);
  const r = await setCache(k, brand);
  console.log("brand cache set cache result=>", r);
  return brand;
};

export const getBrand = async (brandId: string): Promise<Brand> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandRequest();

  req.setId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.getBrand(req, getMetadata(), getOptionsOfLight(), (err, res) => {
      if (!isNull(err)) {
        reject(wrapperGrpcError(err.details));
        return;
      }
      const opRet = res.getOpRet()!;
      if (!opRet.getSuccess()) {
        reject(wrapperGrpcError(opRet.getMsg()));
        return;
      }
      resolve(brandPbToObject(res.getBrand()!));
    });
  });
};

export const getBrandByRemotePKSafely = async (
  remotePK: string
): Promise<Brand> => {
  const v = await getBrandByRemotePK(remotePK);
  return v!;
};

export const getBrandByRemotePK = async (
  remotePK: string
): Promise<Brand | undefined> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandByRemotePKRequest();

  req.setRemotePk(Number(remotePK));

  return new Promise((resolve, reject) => {
    cl.getBrandByRemotePK(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          if (opRet.getCode() === GrpcErrCode.NOT_FOUND) {
            resolve(undefined);
            return;
          } else {
            reject(wrapperGrpcError(opRet.getMsg()));
            return;
          }
        }
        resolve(brandPbToObject(res.getBrand()!));
      }
    );
  });
};

export const listUnmappingedBilinBrands = async (
  userId: string
): Promise<BilinBrand[]> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.ListUnmappingedBilinBrandsRequest();

  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.listUnmappingedBilinBrands(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(res.getBilinBrandsList().map(bilinBrandPbToObject));
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_endorsement entity                           */
/****************************************************************************************/

const brandExtPbToObject = (pb: brandpb.BrandExtEntity): BrandExt => {
  return {
    brandExtId: `${pb.getId()}`,
    brandId: `${pb.getBrandId()}`,
    ownUserId: `${pb.getOwnUserId()}`,
    webposToken: pb.getWebposToken(),
    webposTokenRefreshed: pbTimestampToJs(pb.getWebposTokenRefreshed())!,
    isWxpayAvailable: pb.getIsWxpayAvailable(),
  };
};

/****************************************************************************************/
/*                                   brand_ext cmd                                      */
/****************************************************************************************/

export const refreshWebposToken = async ({
  brandExtId,
  webposToken,
  webposTokenRefreshed,
}: RefreshWebposTokenParams): Promise<BrandExt> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.RefreshWebposTokenRequest();
  req.setId(Number(brandExtId));
  req.setWebposToken(webposToken);
  req.setWebposTokenRefreshed(jsTimestampToPb(webposTokenRefreshed));

  return new Promise((resolve, reject) => {
    cl.refreshWebposToken(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(brandExtPbToObject(res.getBrandExt()!));
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_ext query                                    */
/****************************************************************************************/

// obsolute
/*
export const getBrandWebTokenSafely = async (
  brandId: string
): Promise<string> => {
  // 正常流程的品牌创建必然会同时创建brandExt,所以一定会有数据
  let v = (await getBrandExtByBrandID(brandId))!;
  let webposToken = v.webposToken;
  if (
    isEmpty(webposToken) ||
    Date.now() - v.webposTokenRefreshed > 13 * 86400 * 1000
  ) {
    // 需要刷新下webtoken
    console.log("需要刷新下webtoken...");
    let ownUser = await getUser(v.ownUserId);
    // const brand = await getBrand(brandId);
    let uniqueToken: string = ownUser.memberToken!;
    console.log("ownUser.uniqueToken=>", ownUser.memberToken!);
    // console.log("sg_id=>", brand.remotePK);
    const brandRemotePk = await getBrandRemotePkCachelly(brandId);
    console.log("sg_id=>", brandRemotePk);
    try {
      webposToken = await genWebToken(uniqueToken)
        .setBody({ sg_id: Number(brandRemotePk) })
        .exec();
    } catch (err) {
      console.log(
        "getBrandWebTokenSafely err",
        err instanceof UniqueTokenExpiredError
      );
      if (err instanceof UniqueTokenExpiredError) {
        console.log("getBrandWebTokenSafely处理UniqueTokenExpiredError...");
        uniqueToken = await err.compensate({
          mobile: ownUser.mobile!,
          openId: ownUser.openId,
        });
        ownUser = await updateMemberToken({
          userId: ownUser.userId,
          memberToken: uniqueToken,
        });
        webposToken = await genWebToken(uniqueToken)
          .setBody({ sg_id: Number(brandRemotePk) })
          .exec();
        console.log("补偿更新uniqueToken=>", uniqueToken);
      } else {
        throw err; // unknown error, rethrow it (**)
      }
    }

    v = await refreshWebposToken({
      brandExtId: v.brandExtId,
      webposToken,
      webposTokenRefreshed: Date.now(),
    });
  }
  return webposToken;
};
*/

export const getBrandExtByBrandID = async (
  brandId: string
): Promise<BrandExt | undefined> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandExtByBrandIDRequest();

  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.getBrandExtByBrandID(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          if (opRet.getCode() === GrpcErrCode.NOT_FOUND) {
            resolve(undefined);
            return;
          } else {
            reject(wrapperGrpcError(opRet.getMsg()));
            return;
          }
        }
        resolve(brandExtPbToObject(res.getBrandExt()!));
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_endorsement entity                           */
/****************************************************************************************/

export const getDefaultBrandEndorsement = (
  brandId: string
): BrandEndorsement => {
  const v = {
    brandId,
    isOpen: false,
    spokesmanTitle: "代言人",
    userStratified: UserStratification.ANYONE,
    needApplyInfo: false,
    isEntranceDisplay: false,
    isAutoApproved: false,
  };
  return v;
};

const brandEndorsementPbToObject = (
  pb: brandpb.BrandEndorsementEntity
): BrandEndorsement => {
  return {
    brandId: `${pb.getBrandId()}`,
    isOpen: pb.getIsOpen(),
    spokesmanTitle: pb.getSpokesmanTitle(),
    userStratified: pb.getUserStratified() as UserStratification,
    consumerLowerExpense: pb.getConsumerLowerExpense(),
    needApplyInfo: pb.getNeedApplyInfo(),
    applyInfoFields: pb.getApplyInfoFieldsList(),
    isEntranceDisplay: pb.getIsEntranceDisplay(),
    isAutoApproved: pb.getIsAutoApproved(),
  };
};

/****************************************************************************************/
/*                                   brand_endorsement cmd                              */
/****************************************************************************************/

export const upsertBrandEndorsement = async ({
  brandId,
  isOpen,
  spokesmanTitle,
  userStratified,
  consumerLowerExpense,
  needApplyInfo,
  applyInfoFields,
  isEntranceDisplay,
  isAutoApproved,
}: UpsertBrandEndorsementParams): Promise<BrandEndorsement> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.UpsertBrandEndorsementRequest();

  req.setBrandId(Number(brandId));
  req.setIsOpen(isOpen);
  req.setSpokesmanTitle(spokesmanTitle);
  req.setUserStratified(userStratified);
  if (!isUndefined(consumerLowerExpense)) {
    req.setConsumerLowerExpense(consumerLowerExpense);
  }
  req.setNeedApplyInfo(needApplyInfo);
  if (!isUndefined(applyInfoFields)) {
    req.setApplyInfoFieldsList(applyInfoFields);
  }
  req.setIsEntranceDisplay(isEntranceDisplay);
  req.setIsAutoApproved(isAutoApproved);

  return new Promise((resolve, reject) => {
    cl.upsertBrandEndorsement(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(brandEndorsementPbToObject(res.getBrandEndorsement()!));
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_endorsement query                            */
/****************************************************************************************/
export const getBrandEndorsementByBrandIDSafely = async (
  brandId: string
): Promise<BrandEndorsement> => {
  let v = await getBrandEndorsementByBrandID(brandId);
  if (isUndefined(v)) {
    v = getDefaultBrandEndorsement(brandId);
  }
  return v;
};
export const getBrandEndorsementByBrandID = async (
  brandId: string
): Promise<BrandEndorsement | undefined> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandEndorsementByBrandIDRequest();

  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.getBrandEndorsementByBrandID(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          if (opRet.getCode() === GrpcErrCode.NOT_FOUND) {
            resolve(undefined);
            return;
          } else {
            reject(wrapperGrpcError(opRet.getMsg()));
            return;
          }
        }
        resolve(brandEndorsementPbToObject(res.getBrandEndorsement()!));
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_distribute_endorsement entity                */
/****************************************************************************************/

export const getDefaultBrandDistributeEndorsement = (
  brandId: string
): BrandDistributeEndorsement => {
  const v = {
    brandId,
    isOpen: false,
    distributorTitle: "分销员",
    userStratified: UserStratification.ANYONE,
    needApplyInfo: false,
    isEntranceDisplay: false,
    isAutoApproved: false,
  };
  return v;
};

const brandDistributeEndorsementPbToObject = (
  pb: brandpb.BrandDistributeEndorsementEntity
): BrandDistributeEndorsement => {
  return {
    brandId: `${pb.getBrandId()}`,
    isOpen: pb.getIsOpen(),
    distributorTitle: pb.getDistributorTitle(),
    userStratified: pb.getUserStratified() as UserStratification,
    consumerLowerExpense: pb.getConsumerLowerExpense(),
    needApplyInfo: pb.getNeedApplyInfo(),
    applyInfoFields: pb
      .getApplyInfoFieldsList()
      .map((v) => v as BrandDistributionEndorseApplyField),
    isEntranceDisplay: pb.getIsEntranceDisplay(),
    isAutoApproved: pb.getIsAutoApproved(),
  };
};

/****************************************************************************************/
/*                                   brand_distribute_endorsement cmd                   */
/****************************************************************************************/

export const upsertBrandDistributeEndorsement = async ({
  brandId,
  isOpen,
  distributorTitle,
  userStratified,
  consumerLowerExpense,
  needApplyInfo,
  applyInfoFields,
  isEntranceDisplay,
  isAutoApproved,
}: UpsertBrandDistributeEndorsementParams): Promise<BrandDistributeEndorsement> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.UpsertBrandDistributeEndorsementRequest();

  req.setBrandId(Number(brandId));
  req.setIsOpen(isOpen);
  req.setDistributorTitle(distributorTitle);
  req.setUserStratified(userStratified);
  if (!isUndefined(consumerLowerExpense)) {
    req.setConsumerLowerExpense(consumerLowerExpense);
  }
  req.setNeedApplyInfo(needApplyInfo);
  if (!isUndefined(applyInfoFields)) {
    req.setApplyInfoFieldsList(applyInfoFields);
  }
  req.setIsEntranceDisplay(isEntranceDisplay);
  req.setIsAutoApproved(isAutoApproved);

  return new Promise((resolve, reject) => {
    cl.upsertBrandDistributeEndorsement(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          brandDistributeEndorsementPbToObject(
            res.getBrandDistributeEndorsement()!
          )
        );
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_distribute_endorsement query                 */
/****************************************************************************************/
export const getBrandDistributeEndorsementByBrandIDSafely = async (
  brandId: string
): Promise<BrandDistributeEndorsement> => {
  let v = await getBrandDistributeEndorsementByBrandID(brandId);
  if (isUndefined(v)) {
    v = getDefaultBrandDistributeEndorsement(brandId);
  }
  return v;
};
export const getBrandDistributeEndorsementByBrandID = async (
  brandId: string
): Promise<BrandDistributeEndorsement | undefined> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandDistributeEndorsementByBrandIDRequest();

  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.getBrandDistributeEndorsementByBrandID(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          if (opRet.getCode() === GrpcErrCode.NOT_FOUND) {
            resolve(undefined);
            return;
          } else {
            reject(wrapperGrpcError(opRet.getMsg()));
            return;
          }
        }
        resolve(
          brandDistributeEndorsementPbToObject(
            res.getBrandDistributeEndorsement()!
          )
        );
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_distribute_supply_setting entity             */
/****************************************************************************************/

export const getDefaultBrandDistributeSupplySetting = (
  brandId: string
): BrandDistributeSupplySetting => {
  const v = {
    brandId,
    isAutoApproved: false,
    deliveryModes: [],
    isUseWarehouseShippingFeeRule: false,
    warehouseIds: [],
  };
  return v;
};

const brandDistributeSupplySettingPbToObject = (
  pb: brandpb.BrandDistributeSupplySettingEntity
): BrandDistributeSupplySetting => {
  return {
    brandId: `${pb.getBrandId()}`,
    deliveryModes: pb.getDeliveryModesList().map((v) => v as DeliveryMode),
    isUseWarehouseShippingFeeRule: pb.getIsUseWarehouseShippingFeeRule(),
    unifiedShippingFee: pb.getUnifiedShippingFee(),
    warehouseIds: pb.getWarehouseIdsList().map(toString),
  };
};

/****************************************************************************************/
/*                                   brand_distribute_supply_setting cmd                */
/****************************************************************************************/

export const upsertBrandDistributeSupplySetting = async ({
  brandId,
  deliveryModes,
  isUseWarehouseShippingFeeRule,
  unifiedShippingFee,
  warehouseIds,
}: UpsertBrandDistributeSupplySettingParams): Promise<BrandDistributeSupplySetting> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.UpsertBrandDistributeSupplySettingRequest();

  req.setBrandId(Number(brandId));
  req.setDeliveryModesList(deliveryModes);
  req.setIsUseWarehouseShippingFeeRule(isUseWarehouseShippingFeeRule);
  if (!isUndefined(unifiedShippingFee)) {
    req.setUnifiedShippingFee(unifiedShippingFee);
  }
  req.setWarehouseIdsList(warehouseIds.map(Number));

  return new Promise((resolve, reject) => {
    cl.upsertBrandDistributeSupplySetting(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          brandDistributeSupplySettingPbToObject(
            res.getBrandDistributeSupplySetting()!
          )
        );
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_distribute_supply_setting query              */
/****************************************************************************************/
export const getBrandDistributeSupplySettingByBrandIDSafely = async (
  brandId: string
): Promise<BrandDistributeSupplySetting> => {
  let v = await getBrandDistributeSupplySettingByBrandID(brandId);
  if (isUndefined(v)) {
    v = getDefaultBrandDistributeSupplySetting(brandId);
  }
  return v;
};
export const getBrandDistributeSupplySettingByBrandID = async (
  brandId: string
): Promise<BrandDistributeSupplySetting | undefined> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandDistributeSupplySettingByBrandIDRequest();

  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.getBrandDistributeSupplySettingByBrandID(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          if (opRet.getCode() === GrpcErrCode.NOT_FOUND) {
            resolve(undefined);
            return;
          } else {
            reject(wrapperGrpcError(opRet.getMsg()));
            return;
          }
        }
        resolve(
          brandDistributeSupplySettingPbToObject(
            res.getBrandDistributeSupplySetting()!
          )
        );
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_distribute_product cmd                       */
/****************************************************************************************/

export const upsertBrandDistributeProduct = async ({
  brandId,
  productId,
  percentSale,
  percentOff,
}: UpsertBrandDistributeProductParams): Promise<ProductWithDistributeInfo> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.UpsertBrandDistributeProductRequest();

  req.setBrandId(Number(brandId));
  req.setProductId(Number(productId));
  req.setPercentSale(percentSale);
  req.setPercentOff(percentOff);

  return new Promise((resolve, reject) => {
    cl.upsertBrandDistributeProduct(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          productWithDistributeInfoPbToObject(
            res.getProductWithDistributeInfo()!
          )
        );
      }
    );
  });
};

export const batchUpsertBrandDistributeProducts = async ({
  brandId,
  percentSale,
  percentOff,
}: BatchUpsertBrandDistributeProductsParams): Promise<boolean> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.BatchUpsertBrandDistributeProductsRequest();

  req.setBrandId(Number(brandId));
  req.setPercentSale(percentSale);
  req.setPercentOff(percentOff);

  return new Promise((resolve, reject) => {
    cl.batchUpsertBrandDistributeProducts(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(true);
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand_distribute_product query                     */
/****************************************************************************************/

export interface ListBrandProductWithDistributeInfosParam {
  brandId: string;
  productGroupId?: string;
  keyword?: string;
}

export const listBrandProductWithDistributeInfos = async (
  {
    brandId,
    productGroupId,
    keyword,
  }: ListBrandProductWithDistributeInfosParam,
  pager: PageParams
): Promise<PagedList<ProductWithDistributeInfo>> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.ListBrandProductWithDistributeInfosRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setTs(jsTimestampToPb(pager.ts));
  req.setBrandId(Number(brandId));
  if (!isUndefined(productGroupId)) {
    req.setProductGroupId(Number(productGroupId));
  }
  if (!isUndefined(keyword)) {
    req.setKeyword(keyword);
  }

  return new Promise((resolve, reject) => {
    cl.listBrandProductWithDistributeInfos(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<ProductWithDistributeInfo> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res
          .getProductWithDistributeInfosList()
          .map(productWithDistributeInfoPbToObject);
        resolve(ret);
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand joins entity                                 */
/****************************************************************************************/

const brandWithExtAndStatPbToObject = (
  pb: brandpb.BrandWithExtAndStatEntity
): BrandWithExtAndStat => {
  return {
    // brand
    brandId: `${pb.getId()}`,
    name: pb.getName(),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    remotePK: `${pb.getRemotePk()}`,
    // brand_stat
    ownUserId: `${pb.getOwnUserId()}`,
    isWxpayAvailable: pb.getIsWxpayAvailable(),
    isDuringApplication: pb.getIsDuringApplication(),
    // brand_stat
    fansCount: pb.getFansCount(),
    adminCount: pb.getAdminCount(),
    publishActivityCount: pb.getPublishActivityCount(),
    spokesmanCount: pb.getSpokesmanCount(),
    endorseRequestToAuditCount: pb.getEndorseRequestToAuditCount(),
  };
};

const brandWithExtAndStatAndEndorsementPbToObject = (
  pb: brandpb.BrandWithExtAndStatAndEndorsementEntity
): BrandWithExtAndStatAndEndorsement => {
  return {
    // brand
    brandId: `${pb.getId()}`,
    name: pb.getName(),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    remotePK: `${pb.getRemotePk()}`,
    // brand_stat
    ownUserId: `${pb.getOwnUserId()}`,
    isWxpayAvailable: pb.getIsWxpayAvailable(),
    isDuringApplication: pb.getIsDuringApplication(),
    // brand_stat
    fansCount: pb.getFansCount(),
    adminCount: pb.getAdminCount(),
    publishActivityCount: pb.getPublishActivityCount(),
    spokesmanCount: pb.getSpokesmanCount(),
    endorseRequestToAuditCount: pb.getEndorseRequestToAuditCount(),
    // distribute
    isDistributionEntranceDisplay: pb.getIsDistributionEntranceDisplay(),
    distributorId: toString(pb.getDistributorId()),
    // endorsement
    isEndorsementEntranceDisplay: pb.getIsEndorsementEntranceDisplay(),
    endorseSpokesmanId: toString(pb.getEndorseSpokesmanId()),
  };
};

const activityTypeAsFeatureAvailablePbToObject = (
  pb: brandpb.ActivityTypeAsFeatureAvailableEntity
): ActivityAsFeatureAvailable => {
  return {
    activityType: pb.getActivityType() as ActivityType,
    isAvailable: pb.getIsAvailable(),
  };
};

const brandWithStatPbToObject = (
  pb: brandpb.BrandWithStatEntity
): BrandWithStat => {
  return {
    // brand
    brandId: `${pb.getId()}`,
    name: pb.getName(),
    intro: pb.getIntro(),
    pic: pb.getPic(),
    homepageBg: pb.getHomepageBg(),
    tel: pb.getTel(),
    opened: pbTimestampToJs(pb.getOpened()),
    remotePK: `${pb.getRemotePk()}`,
    // brand_stat
    fansCount: pb.getFansCount(),
    adminCount: pb.getAdminCount(),
    publishActivityCount: pb.getPublishActivityCount(),
    spokesmanCount: pb.getSpokesmanCount(),
    endorseRequestToAuditCount: pb.getEndorseRequestToAuditCount(),
  };
};

/****************************************************************************************/
/*                                   brand joins query                                  */
/****************************************************************************************/

export const getBrandWithExtAndStat = async (
  brandId: string
): Promise<BrandWithExtAndStat> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandWithExtAndStatRequest();

  req.setId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.getBrandWithExtAndStat(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(brandWithExtAndStatPbToObject(res.getBrandWithExtAndStat()!));
      }
    );
  });
};

export const getBrandWithExtAndStatAndEndorsementForUser = async (
  brandId: string,
  userId: string
): Promise<GetBrandWithExtAndStatAndEndorsementForUserRet> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetBrandWithExtAndStatAndEndorsementForUserRequest();

  req.setId(Number(brandId));
  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.getBrandWithExtAndStatAndEndorsementForUser(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: GetBrandWithExtAndStatAndEndorsementForUserRet = {
          brandWithExtAndStatAndEndorsement:
            brandWithExtAndStatAndEndorsementPbToObject(
              res.getBrandWithExtAndStatAndEndorsement()!
            ),
          activityAsFeatureAvailables: res
            .getActivityAsFeatureAvailablesList()
            .map(activityTypeAsFeatureAvailablePbToObject),
        };
        resolve(ret);
      }
    );
  });
};

export const listBrandWithStatsByUserIDAsAdmin = async (
  userId: string,
  pager: PageParams
): Promise<PagedList<BrandWithStat>> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.ListBrandWithStatsByUserIDAsAdminRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setUserId(Number(userId));

  return new Promise((resolve, reject) => {
    cl.listBrandWithStatsByUserIDAsAdmin(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<BrandWithStat> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res
          .getBrandWithStatsList()
          .map((it) => brandWithStatPbToObject(it));
        resolve(ret);
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand account entity                               */
/****************************************************************************************/

const brandSettlementAccountInfoPbToObject = (
  pb: brandpb.BrandSettlementAccountInfoEntity
): BrandSettlementAccountInfo => {
  return {
    brandId: `${pb.getBrandId()}`,
    availableAmount: pb.getAvailableAmount(),
    pendingAmount: pb.getPendingAmount(),
    withdrawingAmount: pb.getWithdrawingAmount(),
    withdrawedAmount: pb.getWithdrawedAmount(),
    accountBank: pb.getAccountBank(),
    accountNumberSuffix: pb.getAccountNumberSuffix(),
  };
};

const brandAccountWithdrawApplymentPbToObject = (
  pb: brandpb.BrandAccountWithdrawApplymentEntity
): BrandAccountWithdrawApplyment => {
  return {
    brandAccountWithdrawApplymentId: `${pb.getId()}`,
    brandId: `${pb.getBrandId()}`,
    userId: `${pb.getUserId()}`,
    applied: pbTimestampToJs(pb.getApplied())!,
    amount: pb.getAmount(),
    remark: pb.getRemark(),
    applyState: pb.getApplyState(),
  };
};

/****************************************************************************************/
/*                                   brand account cmd                                  */
/****************************************************************************************/

export const createBrandAccountWithdrawApplyment = async ({
  brandId,
  userId,
  amount,
  remark,
}: CreateAccountWithdrawApplymentParams): Promise<BrandAccountWithdrawApplyment> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.CreateBrandAccountWithdrawApplymentRequest();

  req.setBrandId(Number(brandId));
  req.setUserId(Number(userId));
  req.setAmount(amount);
  if (!isUndefined(remark)) {
    req.setRemark(remark);
  }

  return new Promise((resolve, reject) => {
    cl.createBrandAccountWithdrawApplyment(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          brandAccountWithdrawApplymentPbToObject(
            res.getBrandAccountWithdrawApplyment()!
          )
        );
      }
    );
  });
};

export const listBrandAccountWithdrawApplyments = async (
  brandId: string,
  pager: PageParams
): Promise<PagedList<BrandAccountWithdrawApplyment>> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.ListBrandAccountWithdrawApplymentsRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.listBrandAccountWithdrawApplyments(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<BrandAccountWithdrawApplyment> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res
          .getBrandAccountWithdrawApplymentsList()
          .map(brandAccountWithdrawApplymentPbToObject);
        resolve(ret);
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand account query                                */
/****************************************************************************************/

export const getBrandSettlementAccountInfo = async (
  brandId: string
): Promise<BrandSettlementAccountInfo> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.GetSettlementAccountInfoRequest();

  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.getSettlementAccountInfo(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        resolve(
          brandSettlementAccountInfoPbToObject(
            res.getBrandSettlementAccountInfo()!
          )
        );
      }
    );
  });
};

/****************************************************************************************/
/*                                   brand account log entity                           */
/****************************************************************************************/

const brandAccountLogPbToObject = (
  pb: messagepb.AccountLogInfo
): AccountLogInfo => {
  return {
    id: `${pb.getId()}`,
    logged: pbTimestampToJs(pb.getLogged())!,
    category: pb.getCategory(),
    amount: pb.getAmount(),
    subject: pb.getSubject(),
    ext_info: pb.getExtInfo(),
  };
};

/****************************************************************************************/
/*                                   brand account log query                            */
/****************************************************************************************/

export const listBrandAccountLogs = async (
  brandId: string,
  pager: PageParams
): Promise<PagedList<AccountLogInfo>> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.ListBrandAccountLogsRequest();

  req.setLimit(pager.limit);
  req.setOffset(pager.offset);
  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.listBrandAccountLogs(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: PagedList<AccountLogInfo> = {
          total: 0,
          items: [],
        };
        ret.total = res.getTotal();
        ret.items = res.getAccountLogsList().map(brandAccountLogPbToObject);
        resolve(ret);
      }
    );
  });
};

export const aggrBrandAccountLogsToInOut = async (
  brandId: string,
  duration: DurationParams
): Promise<AggrBrandAccountLogsToInOutRet> => {
  const cl = getBgsBrandOpSafely();
  const req = new brandpb.AggrBrandAccountLogsToInOutRequest();
  console.log("duration=>", duration);
  req.setBegin(jsTimestampToPb(duration.begin));
  req.setEnd(jsTimestampToPb(duration.end));
  req.setBrandId(Number(brandId));

  return new Promise((resolve, reject) => {
    cl.aggrBrandAccountLogsToInOut(
      req,
      getMetadata(),
      getOptionsOfLight(),
      (err, res) => {
        if (!isNull(err)) {
          reject(wrapperGrpcError(err.details));
          return;
        }
        const opRet = res.getOpRet()!;
        if (!opRet.getSuccess()) {
          reject(wrapperGrpcError(opRet.getMsg()));
          return;
        }
        const ret: AggrBrandAccountLogsToInOutRet = {
          income: res.getIncome(),
          outlay: res.getOutlay(),
        };
        resolve(ret);
      }
    );
  });
};
